package Plugins::MLBGameday::Schedule;

# Copyright Ted Romer
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License,
# version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

use strict;
use warnings;

use base qw(Slim::Plugin::Base);

use MLB::Web;
use MLB::Web::Game;
use Plugins::MLBGameday::Media;
use Plugins::MLBGameday::Util;
use XML::Simple;

### Globals
#

#
# Logging
#
my $log = Slim::Utils::Log->addLogCategory
    ({
        'category'     => 'plugin.mlbgameday',
        'defaultLevel' => 'INFO',
    });


###
#
# Callback cascade to construct OPML menu.  We first
# make an HTTP call to get the days schedule, then make a call
# to get the details (primarily the media information) for 
# each game.

#
# Callback cascade, step 1.  This is the entry point from
# Squeezecenter.  Get the day's schedule.
# 
sub getScheduleCallback {
    reloadOnChange();

    # Make sure this gets called frequently enough ... o/w
    # we might be stuck showing yesterday's games.

    my $client = shift;
    my $gotFeedCallback = shift;
    my @passthrough = @_;         # not used

    # We pass the $state object through the callback cascade
    # to keep track of state and progress.

    my $state = {
        client   => $client,
        callback => $gotFeedCallback,
        wanted   => 0,
        got      => [],
    };

    if ( defined($client) ) {
        my $msg = $client->string( $client->linesPerScreen() == 1 ? 
                                   'PLUGIN_MLBGAMEDAY_LOADING_SHORT' : 
                                   'PLUGIN_MLBGAMEDAY_LOADING_SCHEDULE' );
        $client->block({ 'line' => [ $msg ] });
    }

    # If it is earlier than 6am EDT == 1000 GMT then we want yesterday's games.
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
        gmtime(time - 10 * 3600);
    $mon += 1;
    $year += 1900;

    my $req = miniscoreboard_request
        ({ y => $year, m => $mon, d => $mday });

    my $uri = $req->uri->as_string;
    my $http = Slim::Networking::SimpleAsyncHTTP->
        new(
            sub {
                my $http = shift;
                gotTodaysGamesCallback($state, $req, $http);
                return;
            },
            sub {
                my $http = shift;
                my $error = shift;
                $log->error("Error getting $uri");
                errorCallback($state, $req, $http, $error);
                return;
            },
            );

    $http->get($uri);
    return;
}


#
# Callback cascade, step 2.  Kick off a bunch of requests in parallel
# to get details for each game.
# 
sub gotTodaysGamesCallback {
    my ($state, $req, $http) = @_;

    my $rawResult = XMLin($http->content(), ForceArray => 1);

    my $gameSummaries = {};
    if (!$rawResult) {
        $log->error("No useful data fetching " . $req->uri->as_string);
    } else {
        $gameSummaries = $rawResult->{game};
    }

    my @rawids = keys(%$gameSummaries);

    $state->{wanted} = @rawids;
    $state->{got} = [];

    for my $rawid ( @rawids ) {
        my $summary = $gameSummaries->{$rawid};
        my $id = $rawid;
        $id =~ s@[/-]@_@g;

        my $req = game_detail_request($id);
        my $uri = $req->uri->as_string;

        my $http = Slim::Networking::SimpleAsyncHTTP->
            new(
                sub {
                    my $http = shift;

                    # Parse the result and add it to $state->{got}

                    my $xml = XMLin($http->content(), ForceArray => 1);
                    my $got = $state->{got};
                    push @$got, game_from_id_miniscoreboard_xml($id, $summary, $xml);

                    # Are we done yet?

                    if ( @{$state->{got}} >= $state->{wanted} ) {
                        gotAllGamesCallback($state);
                    }
                    return;
                },
                sub {
                    my $http = shift;
                    my $error = shift;
                    $log->error("Error getting $uri");

                    # Lower our expectations

                    $state->{wanted}--;

                    # Are we done yet?

                    if ( @{$state->{got}} >= $state->{wanted} ) {
                        gotAllGamesCallback($state);
                    }
                    return;
                },
                );
        
        $http->get($uri);
    }

    return;
}

#
# Callback cascade, step 3a.  Success -- construct 
# the OPML and call the original callback.
#
sub gotAllGamesCallback {
    my $state = shift;

    my $client = $state->{client};

    my $mlbgames = $state->{got};

    my @games = ();
    
    # move to Util
    # Sort by league, then by home team
    my $sortfn = sub {
        $log->debug("Comparing " . $a->id() . " to " . $b->id());
        my $result;

        $result = $a->league() cmp $b->league();
        if ( $result != 0 ) {
            $log->debug($result . " based on league");
            return $result;
        }
        $result = $a->home()->nickname() cmp $b->home()->nickname();
        if ( $result != 0 ) {
            $log->debug($result . " based on home nickname");
            return $result;
        }
        # for doubleheaders
        $result = $a->id() cmp $b->id();
        $log->debug($result . " based on game id");
        return $result;
    };

    for my $game ( sort $sortfn @$mlbgames ) {
        push @games, {
            'name'        => gameDisplayLine($game),
            'type'        => 'opml',
            'url'         => \&Plugins::MLBGameday::Media::getMediaCallback,
            'passthrough' => [$game],
            'items'       => [],
        };
    }

    my $feed = {
        'title' => 'MLB Gameday Prototype',
        'type' => 'opml',
        'items' => \@games,
    };

    invokeCallback($state, $feed);

    return;
}

#
# Callback cascade, step 3b. Failed to get the day's schedule, create
# an error message and call the original callback.
#
sub errorCallback {
    my ($state, $req, $http, $error) = @_;

    my $feed = {
        'name'        => "MLB Gameday error",
        'type'        => 'opml',
        'items'       => [
            {name=>$error},
            ],
    };

    invokeCallback($state, $feed);
}

#
# Callback cascade, step 4.  Send the feed -- good or bad -- on its way.
#
sub invokeCallback {
    my($state, $feed) = @_;
    
    my $params = {};
    my $client = $state->{client};

    if ( defined($client) ) {
        # I don't know whether we really need to fill in a params object,
        # but to be safe until I'm no longer superstitious:
        my $tmp = $client->modeParams();
        $params->{'client'} = $client;
        for my $key ( qw(url item search feedtitle timeout remember) ) {
            my $val = $tmp->{$key};
            if ( defined($val) ) {
                $params->{$key} = $val;
            }
        }
        $client->unblock();
    }

    if ( !$state->{done} ) {
        $state->{done} = 1;
        $state->{callback}->($feed, $params);
    } else {
        # Haven't seen this happen here, only for Media case.
        $log->warn("Attempted duplicate callback dropped");
    }

    return;
}

1;

# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
# End:
